from typing import Any, Dict, Optional, Union, List
from datetime import datetime

from sqlalchemy import select, and_, or_
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.notification import Notification
from app.schemas.notification import NotificationCreate, NotificationUpdate


async def get_notification_by_id(db: AsyncSession, *, id: int) -> Optional[Notification]:
    return await db.get(Notification, id)


async def get_notifications_by_recipient(
    db: AsyncSession, *, skip: int = 0, limit: int = 100,
    recipient_id: Optional[int] = None, is_read: Optional[bool] = None,
    notification_type: Optional[str] = None, priority: Optional[str] = None
) -> List[Notification]:
    query = select(Notification)
    if recipient_id:
        query = query.where(Notification.recipient_id == recipient_id)
    if is_read is not None:
        query = query.where(Notification.is_read == is_read)
    if notification_type:
        query = query.where(Notification.notification_type == notification_type)
    if priority:
        query = query.where(Notification.priority == priority)
    query = query.order_by(Notification.created_at.desc())
    query = query.offset(skip).limit(limit)
    result = await db.execute(query)
    return result.scalars().all()


async def create_notification(
    db: AsyncSession, *, obj_in: NotificationCreate, sender_id: Optional[int] = None
) -> Notification:
    db_obj = Notification(
        title=obj_in.title,
        content=obj_in.content,
        notification_type=obj_in.notification_type,
        priority=obj_in.priority,
        receiver_id=obj_in.receiver_id,
        sender_id=sender_id,
        project_id=obj_in.project_id,
        metadata=obj_in.metadata,
    )
    db.add(db_obj)
    await db.commit()
    await db.refresh(db_obj)
    return db_obj


async def update_notification(
    db: AsyncSession, *, db_obj: Notification, obj_in: Union[NotificationUpdate, Dict[str, Any]]
) -> Notification:
    if isinstance(obj_in, dict):
        update_data = obj_in
    else:
        update_data = obj_in.dict(exclude_unset=True)
    
    # 如果通知被标记为已读，自动设置阅读时间
    if update_data.get("is_read") and not db_obj.read_at:
        update_data["read_at"] = datetime.utcnow()
    
    for field in update_data:
        setattr(db_obj, field, update_data[field])
    db.add(db_obj)
    await db.commit()
    await db.refresh(db_obj)
    return db_obj


async def delete_notification(db: AsyncSession, *, id: int) -> Notification:
    notification = await db.get(Notification, id)
    await db.delete(notification)
    await db.commit()
    return notification


async def mark_notification_as_read(db: AsyncSession, *, id: int) -> Notification:
    notification = await db.get(Notification, id)
    if not notification.is_read:
        notification.is_read = True
        notification.read_at = datetime.utcnow()
        db.add(notification)
        await db.commit()
        await db.refresh(notification)
    return notification


async def mark_all_notifications_as_read(db: AsyncSession, *, recipient_id: int) -> int:
    query = select(Notification).where(
        and_(
            Notification.recipient_id == recipient_id,
            Notification.is_read == False
        )
    )
    result = await db.execute(query)
    notifications = result.scalars().all()
    
    count = 0
    for notification in notifications:
        notification.is_read = True
        notification.read_at = datetime.utcnow()
        db.add(notification)
        count += 1
    
    if count > 0:
        await db.commit()
    
    return count